var board = []; //存放数字的数组
var score = 0; //存放得分
var hasConflicted = []; //存放是否冲突的数组
let preValue = []; //存放每一步的操作
let preScore = []; //存放每一步的分数

$(function() {
    newgame();
})

// 初始化
function newgame() {
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            var gridCell = $("#grid_cell_" + i + "_" + j); // 定义变量gridCell来编写每个格子的位置
            gridCell.css("top", getPosTop(i, j));
            gridCell.css("left", getPosLeft(i, j)); //只需定义格子的左上角即可
        }
    }

    for (var i = 0; i < 4; i++) {
        board[i] = [];
        hasConflicted[i] = [];
        for (var j = 0; j < 4; j++) {
            board[i][j] = 0; //把board定义为二维数组来更方便地表达出每个格子，board为0即这个格子没有数字，不为0即有数字
            hasConflicted[i][j] = false; //初始化时需要将每个格子是否冲突的属性定义为false
        }
    }

    updateBoardView(); //定义此函数来将数字放到该格子中

    generateOneNumber(); //每次初始化游戏都会随机产生两个数字
    generateOneNumber();

    score = 0; //初始化
    updateScore(score);
}

function updateBoardView() {
    $(".number_cell").remove(); //由于每一次操作都要调用该函数，所以每次调用之前都要先将上次操作后产生的数字清除
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            $("#grid_container").append("<div class='number_cell' id='number_cell_" + i + "_" + j + "'>"); //给每个格子添加数字
            var theNumberCell = $("#number_cell_" + i + "_" + j); //定义该变量来编写每个格子中数字的css样式
            if (board[i][j] == 0) {
                theNumberCell.css("width", "0"); //当该位置没有数字时，宽高设为0就行
                theNumberCell.css("height", "0");
                theNumberCell.css("top", getPosTop(i, j));
                theNumberCell.css("left", getPosLeft(i, j));
            } else {
                theNumberCell.css("width", "100px");
                theNumberCell.css("height", "100px");
                theNumberCell.css("top", getPosTop(i, j));
                theNumberCell.css("left", getPosLeft(i, j));
                theNumberCell.css("background-color", getNumberBackgroundColor(board[i][j])); //定义该函数来存放不同数字的背景颜色
                theNumberCell.css("color", getNumberColor(board[i][j])); //定义该函数来存放不同数字的颜色
                theNumberCell.text(board[i][j]); //将数字值赋予给相应的格子
            }
            hasConflicted[i][j] = false; //由于每一次操作都要调用该函数，所以每次调用之前都要先将每个格子是否冲突的属性定义为false
        }
    }
}

function undo() {
    let obj = {};
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {

            if (obj[board[i][j]]) {
                obj[board[i][j]]++;
            } else {
                obj[board[i][j]] = 1;
            }
        }
    }
    for (let key in obj) {
        if (key == 0 && obj[key] == 14) {
            return false;
        }
    }
    for (let i = 3; i >= 0; i--) {
        for (let j = 3; j >= 0; j--) {
            board[i][j] = preValue.pop();
        }
    }
    score = preScore.pop();
    updateBoardView();
    updateScore(score);
}

function generateOneNumber() {
    if (nospace(board)) { //先判断还有没有空位置
        return false;
    } else {
        //随机生成一个位置
        var randx = parseInt(Math.floor(Math.random() * 4));
        var randy = parseInt(Math.floor(Math.random() * 4));
        var times = 0;
        while (times < 50) {
            if (board[randx][randy] == 0) {
                break; //判断该位置是否有数字，若没有，则直接跳出循环，若有，则继续生成randx和randy
            } else {
                randx = parseInt(Math.floor(Math.random() * 4));
                randy = parseInt(Math.floor(Math.random() * 4));
            }
            times++;
        }
        if (times == 50) {
            for (var i = 0; i < 4; i++) { //若算法运行了50次后仍然没有找到空位置，则人工采用for循环来找
                for (var j = 0; j < 4; j++) {
                    if (board[i][j] == 0) {
                        randx = i;
                        randy = j;
                    }
                }
            }
        }
        //随机生成一个数字（2或者4）
        var randNumber = Math.random() < 0.5 ? 2 : 4; //当随机数小于0.5时，取2，否则取4
        //把数字放进空位置
        board[randx][randy] = randNumber;
        showNumberWithAnimation(randx, randy, randNumber); //给生成数字的动作添加动画
        return true;
    }
}

$(document).keydown(function(event) {
    switch (event.keyCode) { //键盘敲击事件，此处为keyCode不是keycode
        case 37: //左
            if (moveLeft()) {
                setTimeout("generateOneNumber()", 210); //先要判断是否能够向左动，向左动了后会随机产生一个数字，并判断游戏是否结束
                setTimeout("isgameover()", 300);
            }
            break;
        case 38: //上
            if (moveTop()) {
                setTimeout("generateOneNumber()", 210);
                setTimeout("isgameover()", 300);
            }
            break;
        case 39: //右
            if (moveRight()) {
                setTimeout("generateOneNumber()", 210);
                setTimeout("isgameover()", 300);
            }
            break;
        case 40: //下
            if (moveDown()) {
                setTimeout("generateOneNumber()", 210);
                setTimeout("isgameover()", 300);
            }
            break;
        default:
            break;
    }
})

function isgameover() {
    if (nospace(board) && nomove(board)) {
        alert("game over"); //当没有空位且不能移动时游戏结束
    }
}

function moveLeft() {
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
            preValue.push(board[i][j]);
        }
    }
    preScore.push(score);
    if (canMoveLeft(board)) {
        for (var i = 0; i < 4; i++) {
            for (var j = 1; j < 4; j++) {
                if (board[i][j] != 0) {
                    for (var k = 0; k < j; k++) {
                        if (board[i][k] == 0 && noBlockHorizontal(i, k, j, board)) { //寻找可以移动到的位置board[i][k]
                            showMoveAnimation(i, j, i, k); //添加移动时的动画
                            board[i][k] = board[i][j];
                            board[i][j] = 0;
                            continue;
                        } else if (board[i][k] == board[i][j] && noBlockHorizontal(i, k, j, board) && !hasConflicted[i][k]) {
                            showMoveAnimation(i, j, i, k);
                            board[i][k] += board[i][j];
                            board[i][j] = 0;
                            score += board[i][k]; //分数叠加
                            updateScore(score); //将分数动态传回#score
                            hasConflicted[i][k] = true; //每一次移动后都要将该位置是否存在冲突的属性设为true
                            continue;
                        }
                    }
                }
            }
        }
        setTimeout("updateBoardView()", 200); //由于每一次操作,board都会发生变化，所以每一次操作都要调用updateBoardView()
        return true;
    } else {
        return false;
    }
}

function moveRight() {
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
            preValue.push(board[i][j]);
        }
    }
    preScore.push(score);
    if (canMoveRight(board)) {
        for (var i = 0; i < 4; i++) {
            for (var j = 2; j >= 0; j--) {
                if (board[i][j] != 0) {
                    for (var k = 3; k > j; k--) {
                        if (board[i][k] == 0 && noBlockHorizontal(i, j, k, board)) {
                            showMoveAnimation(i, j, i, k);
                            board[i][k] = board[i][j];
                            board[i][j] = 0;
                            continue;
                        } else if (board[i][k] == board[i][j] && noBlockHorizontal(i, j, k, board) && !hasConflicted[i][k]) {
                            showMoveAnimation(i, j, i, k);
                            board[i][k] += board[i][j];
                            board[i][j] = 0;
                            score += board[i][k];
                            updateScore(score);
                            hasConflicted[i][k] = true;
                            continue;
                        }
                    }
                }
            }
        }
        setTimeout("updateBoardView()", 200);
        return true;
    } else {
        return false;
    }
}

function moveDown() {
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
            preValue.push(board[i][j]);
        }
    }
    preScore.push(score);
    if (canMoveDown(board)) {
        for (var i = 2; i >= 0; i--) {
            for (var j = 0; j < 4; j++) {
                if (board[i][j] != 0) {
                    for (var k = 3; k > i; k--) {
                        if (board[k][j] == 0 && noBlockVertical(j, i, k, board)) {
                            showMoveAnimation(i, j, k, j);
                            board[k][j] = board[i][j];
                            board[i][j] = 0;
                            continue;
                        } else if (board[k][j] == board[i][j] && noBlockVertical(j, i, k, board) && !hasConflicted[k][j]) {
                            showMoveAnimation(i, j, k, j);
                            board[k][j] += board[i][j];
                            board[i][j] = 0;
                            score += board[k][j];
                            updateScore(score);
                            hasConflicted[k][j] = true;
                            continue;
                        }
                    }
                }
            }
        }
        setTimeout("updateBoardView()", 200);
        return true;
    } else {
        return false;
    }
}

function moveTop() {
    for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
            preValue.push(board[i][j]);
        }
    }
    preScore.push(score);
    if (canMoveTop(board)) {
        for (var i = 1; i < 4; i++) {
            for (var j = 0; j < 4; j++) {
                if (board[i][j] != 0) {
                    for (var k = 0; k < i; k++) {
                        if (board[k][j] == 0 && noBlockVertical(j, k, i, board)) {
                            showMoveAnimation(i, j, k, j);
                            board[k][j] = board[i][j];
                            board[i][j] = 0;
                            continue;
                        } else if (board[k][j] == board[i][j] && noBlockVertical(j, k, i, board) && !hasConflicted[k][j]) {
                            showMoveAnimation(i, j, k, j);
                            board[k][j] += board[i][j];
                            board[i][j] = 0;
                            score += board[k][j];
                            updateScore(score);
                            hasConflicted[k][j] = true;
                            continue;
                        }
                    }
                }
            }
        }
        setTimeout("updateBoardView()", 200);
        return true;
    } else {
        return false;
    }
}